// Copyright 2024 Fries_I23
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import 'dart:async';

import 'package:async/async.dart';
import 'package:flutter/foundation.dart';

import '../common/async.dart';
import '../logging/helper.dart';
import '../providers/commons.dart';
import 'db/db_helper.dart';
import 'db/handlers/habit.dart';
import 'db/handlers/record.dart';
import 'db/handlers/sync.dart';

class DBHelperViewModel extends ChangeNotifier
    implements ProviderMounted, AsyncInitialization {
  final DBHelper local;

  CancelableCompleter<bool?>? _completer;
  bool _mounted = true;

  DBHelperViewModel() : local = DBHelper();

  CancelableCompleter<bool?> doInit(
      {required bool reinit,
      Duration timeout = kDebugMode
          ? const Duration(seconds: 600)
          : const Duration(seconds: 60)}) {
    final completer = CancelableCompleter<bool>();
    local
        .init(reinit: reinit)
        .timeout(timeout)
        .then((_) => completer.complete(true))
        .onError((e, s) {
      if (!completer.isCompleted) {
        e != null ? completer.completeError(e, s) : completer.complete(false);
      }
      if (e != null) return Future.error(e, s);
    });
    return completer;
  }

  @override
  Future<bool> init() {
    if (_completer != null) {
      return _completer!.operation.value.then((value) => value ?? false);
    }
    _completer = doInit(reinit: false);
    return _completer?.operation.value.then((value) => value ?? false) ??
        Future.value(false);
  }

  @override
  void dispose() {
    _mounted = false;
    if (inited) local.dispose();
    if (_completer?.isCompleted != true) _completer?.operation.cancel();
    super.dispose();
  }

  Future<void> reload() async {
    if (_completer?.isCompleted != true) await _completer?.operation.cancel();
    _completer = doInit(reinit: true);
    await _completer?.operation.value;
    notifyListeners();
  }

  @override
  bool get mounted => _mounted;

  bool get inited => _completer?.isCompleted ?? false;

  @override
  String toString() {
    return "$runtimeType[$hashCode](local=$local,mounted=$mounted,"
        "inited=$inited)";
  }
}

abstract mixin class DBHelperLoadedMixin {
  DBHelper? _lastLocalHelper;

  late HabitDBHelper habitDBHelper;
  late RecordDBHelper recordDBHelper;
  late SyncDBHelper syncDBHelper;

  void updateDBHelper(DBHelperViewModel newHelper) {
    final localHelper = newHelper.local;
    if (localHelper != _lastLocalHelper) {
      appLog.load.info("$runtimeType.updateDBHelper.local", ex: [newHelper]);
      habitDBHelper = HabitDBHelper(newHelper.local);
      recordDBHelper = RecordDBHelper(newHelper.local);
      syncDBHelper = SyncDBHelper(newHelper.local);
      _lastLocalHelper = localHelper;
    }
  }
}
